package udpSve;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;

public class UDPClientNode extends AbstractNodeKomunikacija {

	private int requestID = 1;
	List<Entry<InetAddress, Integer>> sviServeri;
	Random myRandomizer = new Random();
	WorkerProstiBrojevi worker;
	volatile int currentChunk, requestChunkID;
	private Map<Integer, String> zahtevi;
	volatile boolean requestedChunk, startedWorkigOnChunk, imaChunkova, ostaloProstihDaSePrebace;
	volatile long requestChunkTimestamp;
	Map<Integer, List<Integer>> prostiPoChunku;
	private int brojChunkova;

	public UDPClientNode(int requestID, String opseg, int listenPort,
			List<Entry<InetAddress, Integer>> sviServeri) {
		super(listenPort);
		this.requestID = requestID;
		this.sviServeri = sviServeri;
		this.zahtevi = new ConcurrentHashMap<Integer, String>();
		this.currentChunk = -1;
		this.startedWorkigOnChunk = false;
		this.imaChunkova = true;
		this.ostaloProstihDaSePrebace = true;
		this.worker = new WorkerProstiBrojevi();
		this.prostiPoChunku = new ConcurrentHashMap<Integer, List<Integer>>();
		String[] split = opseg.split(",");
		int pocetakOpsega = Integer.parseInt(split[0]);
		int krajOpsega = Integer.parseInt(split[1]);
		brojChunkova = (krajOpsega - pocetakOpsega) / Commmands.VELICINA_CHUNKA;
		if ((krajOpsega - pocetakOpsega) % Commmands.VELICINA_CHUNKA != 0)
			brojChunkova++;
		System.out.println("PRAVIM NOVI CLIENT NODE");
	}

	public void start() {
		worker.start();
		startRecieving();
		Thread periodicnoRadjenjeStvari = new Thread(new Runnable() {

			@Override
			public void run() {
				while (imaChunkova) {
					if (!worker.isWorking()) {
						if (startedWorkigOnChunk) {
							// radio je a zavrsio sada javi serveru sve
							// proste...
							long timestamp = System.currentTimeMillis();
							// List<Integer> prostiOdMene = worker.getProsti();
							// POSALJI SERVERU
							StringBuilder sb = new StringBuilder();
							for (Integer prost : worker.getProsti()) {
								sb.append(";");
								sb.append(prost);
							}
							Entry<InetAddress, Integer> randomServer = sviServeri.get(myRandomizer
									.nextInt(sviServeri.size()));
							requestChunkID = requestID;
							sendPaket(
									randomServer.getKey(),
									randomServer.getValue(),
									requestID++,
									Commmands.CLIENT_FINISHED_CHUNK,
									currentChunk + ";" + timestamp
											+ sb.toString());
							// System.out.println("CLIENT ZAVRSIO SAM CHUNK: "
							// + worker.getStart() + +worker.getEnd());
							System.out.println("MOJI PROSTI: "
									+ worker.getProsti());
							startedWorkigOnChunk = false;
							requestedChunk = false;
						}
						// AKO NE RADI NA CHUNKU NACI CHUNK OD SERVERA I
						// SETOVATI GA
						else if (requestedChunk) {
							if (zahtevi.containsKey(requestChunkID)) {// JOS MU
																		// NIJE
																		// ODGOVORIO
								if ((System.currentTimeMillis() - requestChunkTimestamp) > 10000) {
									zahtevi.remove(requestChunkID);
									requestedChunk = false;
								}
							} else {
								System.out.println("CLIENT SET CHUNK");
								long timestamp = System.currentTimeMillis();
								worker.setChunk(currentChunk, currentChunk
										* Commmands.VELICINA_CHUNKA + 1,
										(currentChunk + 1)
												* Commmands.VELICINA_CHUNKA);
								startedWorkigOnChunk = true;
								// POSALJI SERVERU DA SAM POCEO DA RADIM

								Entry<InetAddress, Integer> randomServer = sviServeri
										.get(myRandomizer.nextInt(sviServeri
												.size()));
								requestChunkID = requestID;
								sendPaket(randomServer.getKey(),
										randomServer.getValue(), requestID++,
										Commmands.CLIENT_STARTED_CHUNK,
										currentChunk + ";" + timestamp);
								// System.out
								// .println("KLIJENT POCO SAM DA RADIM CHUNK: "
								// + worker.getStart()
								// + " "
								// + worker.getEnd());
							}
						} else if (!requestedChunk) {
							obratiSeServerimaZaChunk();
							continue;
						}
					}
					
					//SLANJE UPITA SERVERU O LISTI SERVERA, I OSTALE INFO(ETA..)
					Entry<InetAddress, Integer> randomServer = sviServeri
							.get(myRandomizer.nextInt(sviServeri
									.size()));
					sendPaket(randomServer.getKey(),
							randomServer.getValue(), requestID++,
							Commmands.GIVE_ME_NEW_INFO,
							"EMPTY");
					//nije bitno stavljati ovaj request u zahteve, jer nije potrebno matchovati ih
					
					try {
						Thread.sleep(5000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				while(ostaloProstihDaSePrebace){
					for (Entry<InetAddress, Integer> entry : sviServeri) {
						for (int i = 0; i < brojChunkova; i++) {
							boolean sviTu=true;
							if(!prostiPoChunku.containsKey(i)){
								sviTu = false;
								
								sendPaket(entry.getKey(),
										entry.getValue(), requestID++,
										Commmands.GIVE_ME_PRIMES,
										Integer.toString(i));//Treba proveriti ako je bas taj puko!
								try {
									Thread.sleep(30);
								} catch (InterruptedException e) {
									// TODO Auto-generated catch block
									e.printStackTrace();
								}
							}
							if(sviTu) ostaloProstihDaSePrebace = false;
							
						}
						
						if(!ostaloProstihDaSePrebace) break;
						
						try {
							Thread.sleep(500);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
				System.out.println(prostiPoChunku);
			}
		});
		periodicnoRadjenjeStvari.start();
		while (true) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	protected void obratiSeServerimaZaChunk() {
		System.out.println("OBRACAM SE SERVERU ZA CHUNK");
		Entry<InetAddress, Integer> randomServer = sviServeri.get(myRandomizer
				.nextInt(sviServeri.size()));
		requestChunkID = requestID;
		sendPaket(randomServer.getKey(), randomServer.getValue(), requestID++,
				Commmands.GIVE_ME_NEW_CHUNK, "EMPTY");
		requestedChunk = true;
		zahtevi.put(requestChunkID, Commmands.HERE_IS_A_CHUNK);
		requestChunkTimestamp = System.currentTimeMillis();
	}

	@Override
	protected void obradiPaket(InetAddress recieveAddress, int recievePort,
			int requestID, String komanda, String telo) {
		if (komanda.equals(Commmands.NEW_NODE)) {
			Entry<InetAddress, Integer> randomServer = sviServeri
					.get(myRandomizer.nextInt(sviServeri.size()));
			sendPaketWithCustomHeader(recieveAddress, recievePort,
					randomServer.getKey(), randomServer.getValue(), requestID,
					komanda, telo);
		}
		else if (komanda.equals(Commmands.HERE_IS_A_CHUNK)) {
			System.err.println("DOBIO SAM CHUNK");
			currentChunk = Integer.parseInt(telo);
			if (currentChunk == -1) {
				//imaChunkova = false; // OVDE NIKAKO NEMA OTPORNOSTI NA GRESKE
										// nveli
				
 				// OBRATI SERVERU DA DOBIJE SVE PROSTE!!!!!
				
				// Djole: ne mora da znaci, mozda su samo neki jos uvek in progress, treba jos da ceka i da pita.
			}
			else{
				System.err.println(requestID);
				zahtevi.remove(requestID);
			}
		}
		else if (komanda.equals(Commmands.ALL_FINISHED)) {
			imaChunkova=false;
			//System.out.println(telo);
		}
		else if (komanda.equals(Commmands.HERE_IS_INFO)) {
			String[] split = telo.split(";");
			String info = split[0];
			System.out.println(info);
			
			int brojServera = Integer.parseInt(split[1]);

			List<Entry<InetAddress, Integer>> sviServeriUpdate = new ArrayList<Map.Entry<InetAddress, Integer>>();
			int j = 2;
			for (int i = 0; i < brojServera; i += 1) {

				try {
					sviServeriUpdate
							.add(new AbstractMap.SimpleEntry<InetAddress, Integer>(
									InetAddress.getByName(split[j]), Integer
											.parseInt(split[j + 1])));
				} catch (NumberFormatException e) {
					e.printStackTrace();
				} catch (UnknownHostException e) {
					e.printStackTrace();
				}
				j += 2;
			}
			
			sviServeri = sviServeriUpdate;//VAZNO MOZE DA SE SJEBE NEGDE ZBOG THREDOVA! Treba skontati.
			
		}else if (komanda.equals(Commmands.HERE_ARE_PRIMES)) {
			String[] split = telo.split(";");
			int chunkId = Integer.parseInt(split[0]);

			ArrayList<Integer> prosti = new ArrayList<Integer>();
			for (int i = 1; i < split.length; i++) {
				int prost = Integer.parseInt(split[i]);
				prosti.add(prost);
			}
			
			prostiPoChunku.put(chunkId, prosti);
		} 

	}
}
